using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using UnityEditor.Experimental.GraphView;
using System.Linq;

public class GraphNodePort : Port
{
    protected GraphNodePort(Orientation portOrientation, Direction portDirection, Capacity portCapacity, Type type) : base(portOrientation, portDirection, portCapacity, type)
    {
    }

    public static new GraphNodePort Create<TEdge>(Orientation orientation, Direction direction, Capacity capacity, Type type) where TEdge : Edge, new()
    {
        var srcPort = Port.Create<TEdge>(orientation, direction, capacity, type);
        var port = new GraphNodePort(orientation, direction, capacity, type)
        {
            m_EdgeConnector = srcPort.edgeConnector
        };
        port.AddManipulator(port.m_EdgeConnector);
        srcPort = null;
        return port;
    }
    //需要Port区域全部都能点到，不用区分文字部分
    public override bool ContainsPoint(Vector2 localPoint)
    {
        return true;
    }

}

public class GraphNodeSelect : EventBase<GraphNodeSelect>
{
    public GraphNode node { get; set; }

    public static GraphNodeSelect GetPooled(GraphNode node)
    {
        var e = GetPooled();
        e.node = node;
        e.target = node.panel.visualTree;
        return e;
    }

    protected override void Init()
    {
        base.Init();
        node = null;
    }
}
public class GraphNodeUnSelect : EventBase<GraphNodeUnSelect> 
{
    public GraphNode node { get; set; }

    public static GraphNodeUnSelect GetPooled(GraphNode node)
    {
        var e = GetPooled();
        e.node = node;
        e.target = node.panel.visualTree;
        return e;
    }

    protected override void Init()
    {
        base.Init();
        node = null;
    }
}

public class GraphNode : Node
{
    public Port InPort { get; protected set; }

    public Port OutPort { get; protected set; }

    public GraphNodeData Data { get; protected set; }

    private EventCallback<MouseDownEvent> mDoubleClickEvent;

    public GraphNode() : base("Assets/BuffEditor/Resources/UXML/GraphNode.uxml")
    {
        Init();
    }

    public GraphNode(GraphNodeData data) : base("Assets/BuffEditor/Resources/UXML/GraphNode.uxml")
    {
        SetData(data);
        Init();
    }

    public void Init()
    {
        InitPorts();
        UpdateClasses();
    }

    public void RegisterDoubleClickCallback(EventCallback<MouseDownEvent> callback)
    {
        if (mDoubleClickEvent == null)
        {
            RegisterCallback<MouseDownEvent>(OnMouseDown);
        }
        mDoubleClickEvent = callback;
    }
    public void UnregisterDoubleClickCallback()
    {
        mDoubleClickEvent = null;
        UnregisterCallback<MouseDownEvent>(OnMouseDown);
    }

    protected virtual void OnMouseDown(MouseDownEvent e)
    {
        if (e.button == (int)MouseButton.LeftMouse
            && e.clickCount == 2)
        {
            mDoubleClickEvent?.Invoke(e);
        }
    }

    protected virtual void InitPorts()
    {
        InPort = GraphNodePort.Create<Edge>(Orientation.Vertical, Direction.Input, Port.Capacity.Multi, typeof(bool));
        InPort.portName = "";
        InPort.style.flexDirection = FlexDirection.Row;
        //InPort.style.flexGrow = 1;
        InPort.Q<Label>("type").RemoveFromHierarchy();
        inputContainer.Add(InPort);

        OutPort = GraphNodePort.Create<Edge>(Orientation.Vertical, Direction.Output, Port.Capacity.Multi, typeof(bool));
        OutPort.portName = "";
        OutPort.style.flexDirection = FlexDirection.Row;
        OutPort.Q<Label>("type").RemoveFromHierarchy();
        //OutPort.style.flexGrow = 1;
        outputContainer.Add(OutPort);
    }

    public void SetData(GraphNodeData data)
    {
        var titleLabel = this.Q<Label>("title-label");
        titleLabel.Unbind();

        titleLabel.Bind(new SerializedObject(data));

        Data = data;
        
        style.left = Data.Position.x;
        style.top = Data.Position.y;
    }

    private void UpdateClasses()
    {
        RemoveFromClassList("error");
        RemoveFromClassList("option");
        RemoveFromClassList("talk");
        RemoveFromClassList("normal");   
        AddToClassList("normal");   
    }

    public void DeleteSelf()
    {
        Scope scope = this.GetContainingScope();
        if (scope != null)
        {
            scope.RemoveElement(this);
        }

        StackNode stack = parent as StackNode;
        if (stack != null)
        {
            stack.RemoveElement(this);
        }

        ClearPorts();
        RemoveFromHierarchy();
    }

    public void ClearPorts()
    {
        InPort.connections.ToList().ForEach(edge =>
        {
            if (edge.input != null) edge.input.Disconnect(edge);
            if (edge.output != null) edge.output.Disconnect(edge);
            edge.RemoveFromHierarchy();

        });
        OutPort.connections.ToList().ForEach(edge =>
        {
            if (edge.input != null) edge.input.Disconnect(edge);
            if (edge.output != null) edge.output.Disconnect(edge);
            edge.RemoveFromHierarchy();

        });
    }

    public List<Edge> GetEdges()
    {
        List<Edge> edges = new List<Edge>();

        foreach (var edge in InPort.connections)
        {
            edges.Add(edge);
        }

        foreach (var edge in OutPort.connections)
        {
            edges.Add(edge);
        }

        return edges;
    }

    public override void OnUnselected()
    {
        base.OnUnselected();
        SendEvent(GraphNodeUnSelect.GetPooled(this));
    }
    public override void OnSelected()
    {
        base.OnSelected();
        SendEvent(GraphNodeSelect.GetPooled(this));
    }

    public GraphNodeData Save()
    {
        Data.Position = new Vector2(style.left.value.value, style.top.value.value);
        Data.InPorts.Clear();
        Data.OutPorts.Clear();
        foreach (var edge in OutPort.connections)
        {
            //找到Input端
            var inputNode = edge.input.node as GraphNode;
            if (inputNode != null)
            {
                Data.OutPorts.Add(inputNode.Data);
            }
        }
        foreach (var edge in InPort.connections)
        {
            var outputNode = edge.output.node as GraphNode;
            if (outputNode != null)
            {
                Data.InPorts.Add(outputNode.Data);
            }
        }
        return Data;
    }
}
